#!/usr/bin/python
# -*- coding: utf-8 -*-

import logging
import os
import urllib
import urlparse
import BaseHTTPServer

import bpmf

CONTENT_TYPE = {
    '.html': 'text/html',
    '.jpg': 'image/jpeg',
    '.js': 'text/javascript',
}
DEFAULT_CONTENT_TYPE = 'text/plain'
CONTENT_ENCODING = 'utf-8'

class KaraokeHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def address_string(self):
        return self.client_address[0]  # do not look up for the name

    def do_GET(self):
        def _ParseParams(params_string):
            params = {}
            if params_string:
                for args in params_string.split('&'):
                    if args:
                        (key, value) = args.split('=')
                        value = urllib.unquote_plus(value).decode(CONTENT_ENCODING)
                        params[key] = value
            return params

        components = urlparse.urlparse(self.path)
        cmd = components[2]
        params = _ParseParams(components[4])

        if cmd == '/add':
            # Add a new song to the queue
            self.server.karaoke.AddToPlayList(
                params['singer_type'], params['singer'], params['song'])
            self._SendOk()
        elif cmd == '/next':
            # Play next song
            self.server.karaoke.Next()
            self._SendOk()
        elif cmd == '/playlist':
            # Return the play list
            playlist = self.server.karaoke.GetPlayList()
            content = self._RenderPlayList(playlist)
            self._SendHtml(content)
        elif cmd == '/singer':
            # Return the list of singers of a particular type
            singer_type = params['singer_type']
            singers = list(self.server.karaoke.GetSingers(singer_type))
            content = self._RenderSingerPage(singer_type, singers)
            self._SendHtml(content)
        elif cmd == '/song':
            # Return the list of songs of a particular singer
            singer_type = params['singer_type']
            singer = params['singer']
            songs = list(self.server.karaoke.GetSongs(singer_type, singer))
            content = self._RenderSongPage(singer_type, singer, songs)
            self._SendHtml(content)
        elif cmd == '/toggle_audio':
            # Toggle original sound track/karaoke
            self.server.karaoke.ToggleAudioChannel()
            self._SendOk()
        elif cmd == '/promote':
            number = int(params['number']) - 1
            self.server.karaoke.Promote(number)
            self._SendOk()
        elif cmd == '/delete':
            number = int(params['number']) - 1
            self.server.karaoke.Delete(number)
            self._SendOk()
        elif cmd == '/shuffle':
            self.server.karaoke.Shuffle()
            self._SendOk()
        elif cmd == '/random_add':
            self.server.karaoke.RandomAdd()
            self._SendOk()
        else:
            filename = cmd
            if filename.endswith('/'):
                filename += 'index.html'

            data_path = os.path.abspath('data')
            file_path = os.path.abspath('data' + filename)
            if file_path.startswith(data_path) and os.path.exists(file_path):
                self._SendFile(file_path)
            else:
                self.send_error(404)
    
    def _RenderPlayList(self, playlist):
        text = u'<h2>已點歌曲</h2><table id="playlist">'
        count = 0
        for singer, song, filename in playlist:
            row_class = ['even', 'odd'][count % 2]
            if count == 0:
                count_str = u'正在播放'
            else:
                count_str = '%d' % count

            text += (u'<tr class="%s">' +
                     u'<td class="number">%s</td>' +
                     u'<td class="song">%s</td>' +
                     u'<td class="singer">%s</td>' +
                     u'<td class="promote">') % (
                row_class, count_str, song, singer)
            if count > 1:
                text += u'<a onclick="promoteSong(%d)">優先</a>' % count
            text += u'</td><td class="delete">'
            if count > 0:
                text += u'<a onclick="deleteSong(%d)">刪除</a>' % count
            text +=  u'</td></tr>'
            count += 1
        text += '</table>'
        text += '<br><br>'
        return text

    def _RenderSingerPage(self, singer_type, singers):
        body = '<h2 class="singer_name">%s</h2>' % singer_type
        current_yin = None
        is_first = True
        for singer in singers:
            yin = bpmf.Query(singer)[:1]
            if yin != current_yin:
                current_yin = yin
                if is_first:
                    is_first = False
                else:
                    body += '</div>'
                body += '<h3>%s</h3><div class="singers">' % yin.upper()
            body += ('<a onclick="getSongs(\'%s\',\'%s\', true)">%s</a> ' % (
                     singer_type, singer, singer))
        if not is_first:
            body += '</div>'
        body += '<br><br>'
        return body

    def _RenderSongPage(self, singer_type, singer, songs):
        body = '<h2 id="singer_name">%s</h2>' % singer

        current_len = 0
        is_first = True
        for song in songs:
            l = len(bpmf.Query(song))
            if l != current_len:
                current_len = l
                if is_first:
                    is_first = False
                else:
                    body += '</div>'
                body += u'<h3>%d 字</h3><div class="songs">' % l
            body += ('<a onclick="addSong(\'%s\', \'%s\', \'%s\')">%s</a> ' % (
                    singer_type, singer, song, song))
        if not is_first:
            body += '</div>'
        body += '<br><br>'
        return body

    def _Send(self, code, content_type, content):
        self.send_response(code)
        self.send_header('Content-type', content_type)
        self.send_header('Cache-Control', 'no-cache')
        self.end_headers()
        self.wfile.write(content)

    def _SendFile(self, filename):
        def _GetContentType(filename):
            dot = filename.rfind('.')
            suffix = filename[dot:]
            if dot == -1 or suffix not in CONTENT_TYPE:
                return DEFAULT_CONTENT_TYPE
            else:
                return CONTENT_TYPE[suffix]

        f = open(filename, 'r')
        content = f.read()
        f.close()
        self._Send(200, _GetContentType(filename), content)

    def _SendHtml(self, content):
        self._Send(200, 'text/html', content.encode(CONTENT_ENCODING))

    def _SendOk(self):
        self._Send(200, 'text/plain', 'ok')


class WebServer(BaseHTTPServer.HTTPServer):
    def __init__(self, config, karaoke):
        self._config = config

        port = int(config['port'])
        BaseHTTPServer.HTTPServer.__init__(self, ('', port), KaraokeHandler)
        self.karaoke = karaoke
        self._keep_running = True
        logging.info('The HTTP server is up')

    def serve_forever(self):
        logging.info('The HTTP server starts serving requests')
        while self._keep_running:
            self.handle_request()

    def shutdown(self):
        self.karaoke.Terminate()
        self._keep_running = False
        logging.info('Shutting down the HTTP server')
